package com.grayscale.core.aspectj;

import com.alibaba.fastjson.JSONObject;
import com.grayscale.annotation.GrayScaleFunction;
import com.grayscale.core.adapt.GrayScaleRuleAdapt;
import com.grayscale.core.config.GrayScaleClientConfig;
import com.grayscale.core.config.GrayScaleZookeeper;
import com.grayscale.core.constant.GrayScaleConstants;
import com.grayscale.core.entity.InterfaceRuleVo;
import com.grayscale.core.factory.GrayScaleRuleFactory;
import com.grayscale.core.util.GrayScaleOfflineUtil;
import com.grayscale.core.util.GrayScalePathBuilder;
import com.grayscale.core.util.HostUtil;
import com.grayscale.core.util.JsonParseUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author liuli
 * @date 2022/2/9
 * 服务调用是 切灰度方法
 */
@Aspect
@Component
public class GrayScaleFunctionAspect{
    private  Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    GrayScaleClientConfig grayScaleClientConfig;
    @Resource
    GrayScaleZookeeper grayScaleZookeeper;

    /**
     * 定义拦截规则
     */
    @Pointcut("@annotation(com.grayscale.annotation.GrayScaleFunction)")
    public void grayScaleMethodPointcut() {

    }

    @Around(value="grayScaleMethodPointcut()")
    public Object grayScaleResource(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String localHost = HostUtil.getLocalHost()+":"+grayScaleClientConfig.getServerPort();
        Method method = getInvokeName(proceedingJoinPoint,localHost);
        if (method == null){
            log.info("GrayScaleFunction Server:{} 方法：{}不切",localHost,this.resolveMethod(proceedingJoinPoint).getName());
            return proceedingJoinPoint.proceed();
        }else{
            //计数
            count(proceedingJoinPoint,localHost);
            log.info("GrayScaleFunction Server:{} 方法：{}，切换到：{}",localHost,this.resolveMethod(proceedingJoinPoint).getName(),method.getName());
            return method.invoke(proceedingJoinPoint.getTarget(), proceedingJoinPoint.getArgs());
        }
    }

    /**
     * 保存切量次数
     * @param proceedingJoinPoint
     * @param localHost
     */
    private void count(ProceedingJoinPoint proceedingJoinPoint, String localHost) {
        if (grayScaleClientConfig.getOfflineMode()){
            log.debug("离线模式开始，不记录调用次数");
        }else{
            Method originMethod = this.resolveMethod(proceedingJoinPoint);
            grayScaleZookeeper.counter(GrayScalePathBuilder.buildAppPath(grayScaleClientConfig.getAppName(),grayScaleClientConfig.getAppCode())
                    +GrayScaleConstants.SERVER+"/"+proceedingJoinPoint.getTarget().getClass().getName()+"#"+originMethod.getName()+GrayScaleConstants.count);
        }
    }

    /***
     * 是否需要切量
     * @param proceedingJoinPoint
     * @return
     */
    private Method getInvokeName(ProceedingJoinPoint proceedingJoinPoint,String localHost) {
        try {
            Method originMethod = this.resolveMethod(proceedingJoinPoint);
            GrayScaleFunction grayScaleFunction = originMethod.getAnnotation(GrayScaleFunction.class);
            log.info("GrayScaleFunction Server:{}",localHost);
            //1.没配置就不切，不能让服务出错
            if (null == grayScaleFunction) {
                log.error("{} 没有配置对应的灰度方法",originMethod.getName());
                return null;
            }
            if (grayScaleClientConfig.getOfflineMode()){
                log.info("Grayscale 开启离线模式");
            }
            //2.系统级别的开关
            Boolean appSwitch = getAppSwitch();
            if (!appSwitch){
                log.info("系统级开关关闭");
                return null;
            }
            //3.接口级别开关
            Boolean functionSwitch = getFunctionSwitch(proceedingJoinPoint,originMethod);
            if (!functionSwitch){
                log.info("方法级开关关闭");
                return null;
            }
            //4.切量规则
            String ruleData = getFunctionRules( proceedingJoinPoint,originMethod);
            if (StringUtils.isBlank(ruleData)){
                log.info("切量规则没有配置");
                return null;
            }
            List<InterfaceRuleVo> originalList = JSONObject.parseArray(ruleData,InterfaceRuleVo.class);
            for (int i = 0; i < originalList.size(); i++) {
                GrayScaleRuleAdapt grayScaleRuleAdapt = GrayScaleRuleFactory.getDealClass(originalList.get(i).getRuleCode());
                if (grayScaleRuleAdapt != null){
                    String key = originalList.get(i).getRuleKey();
                    String keyValue = null;
                    if (StringUtils.isNotBlank(key)){
                        String paramValues = getParam(proceedingJoinPoint);
                        Map pMap = new HashMap();
                        JsonParseUtil.parseJson2Map( pMap,  paramValues, null);
                        if (pMap.containsKey(key)){
                            keyValue =String.valueOf(pMap.get(key));
                        }
                        if (StringUtils.isBlank(keyValue)){
                            log.error("KEY值为空，请考虑key是否合理{} {}",keyValue,JSONObject.toJSONString(originalList.get(i)));
                            return null;
                        }
                    }
                    boolean flag =  grayScaleRuleAdapt.process(originalList.get(i).getRuleValue(),originalList.get(i).getRuleCode(),localHost,keyValue);
                    if (!flag){
                        log.info("不满足规则:{}",JSONObject.toJSONString(originalList.get(i)));
                        return null;
                    }
                }
            }
            //所有规则都满足才 切量
            String methodName = grayScaleFunction.grayScaleMethod();
            if (StringUtils.isBlank(methodName)){
                methodName = originMethod.getName()+GrayScaleConstants.FUNCTION_SUFFIX;
            }
            Method method = this.resolveMethodByName(proceedingJoinPoint, methodName);
            if (method == null){
                log.error("切量方法{}不存在,不切",methodName);
                return null;
            }
            return method;
        } catch (Throwable throwable) {
            log.error("处理切量规则异常{}",throwable);
            return null;
        }
    }

    /**
     * 获取方法 规则
     * @param proceedingJoinPoint
     * @param originMethod
     * @return
     */
    private String getFunctionRules(ProceedingJoinPoint proceedingJoinPoint,Method originMethod){
        String ruleData = null;
        if (grayScaleClientConfig.getOfflineMode()){
            //离线模式,从配置文件读取
            ruleData =   GrayScaleOfflineUtil.readDisk(GrayScalePathBuilder.buildOfflineServerFunctionParametersPath(grayScaleClientConfig,proceedingJoinPoint.getTarget().getClass().getName()+"#"+originMethod.getName()));
        }else{
            //在线模式
            ruleData = grayScaleZookeeper.getPathData(GrayScalePathBuilder.buildAppPath(grayScaleClientConfig.getAppName(),grayScaleClientConfig.getAppCode())
                    +GrayScaleConstants.SERVER+"/"+proceedingJoinPoint.getTarget().getClass().getName()+"#"+originMethod.getName()+ GrayScaleConstants.RULES);
        }
        return ruleData;
    }
    /**
     * 获取方法开关
     * @param proceedingJoinPoint
     * @param originMethod
     * @return
     */
    private Boolean getFunctionSwitch(ProceedingJoinPoint proceedingJoinPoint,Method originMethod) {
        String functionSwitch = null;
        if (grayScaleClientConfig.getOfflineMode()){
            //离线模式,从配置文件读取
            functionSwitch = GrayScaleOfflineUtil.readDisk(GrayScalePathBuilder.buildOfflineFunctionSwitchPath(grayScaleClientConfig,proceedingJoinPoint.getTarget().getClass().getName()+"#"+originMethod.getName()));
        }else{
            //在线模式
            functionSwitch = GrayScalePathBuilder.buildAppPath(grayScaleClientConfig.getAppName(),grayScaleClientConfig.getAppCode())
                    +GrayScaleConstants.SERVER+"/"+proceedingJoinPoint.getTarget().getClass().getName()+"#"+originMethod.getName();
        }
        if (StringUtils.isBlank(functionSwitch)){
            return false;
        }else{
            return true;
        }
    }

    /***
     * 获取系统开关
     * @return
     */
    private Boolean getAppSwitch() {
       String appSwitch = null;
       if (grayScaleClientConfig.getOfflineMode()){
           //离线模式,从配置文件读取
           appSwitch =   GrayScaleOfflineUtil.readDisk(GrayScalePathBuilder.buildOfflineServerSwitchPath(grayScaleClientConfig));
       }else{
           //在线模式
          appSwitch = grayScaleZookeeper.getPathData(GrayScalePathBuilder.buildAppPath(grayScaleClientConfig.getAppName(),grayScaleClientConfig.getAppCode()));
       }
       if (StringUtils.isBlank(appSwitch)){
            return false;
       }else{
            return true;
       }
    }

    /**
     * 获取方法的入参名值
     * @param proceedingJoinPoint
     * @return
     */
    private String getParam(ProceedingJoinPoint proceedingJoinPoint) {
        Map<String, Object> map = new HashMap<String, Object>();
        String[] names = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        Object[] values = proceedingJoinPoint.getArgs();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], values[i]);
        }
        return JSONObject.toJSONString(map);
    }

    private Method resolveMethodByName(ProceedingJoinPoint proceedingJoinPoint, String methodName) {
        try {
            Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
            Method[] methods = targetClass.getDeclaredMethods();
            Method[] var5 = methods;
            int var6 = methods.length;
            for(int var7 = 0; var7 < var6; ++var7) {
                Method method = var5[var7];
                if (method.getName().equals(methodName)) {
                    return method;
                }
            }
        } catch (SecurityException e) {
            return null;
        }
        return null;
    }
    private Method resolveMethod(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature();
        Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
        Method method = this.getDeclaredMethodFor(targetClass, signature.getName(), signature.getMethod().getParameterTypes());
        if (null == method) {
            throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName());
        } else {
            return method;
        }
    }

    private Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException var6) {
            Class<?> superClazz = clazz.getSuperclass();
            return superClazz != null ? this.getDeclaredMethodFor(superClazz, name, parameterTypes) : null;
        }
    }

}
